Global Acceleratorで作成されるセキュリティグループをALBのセキュリティグループで許可するカスタムリソース作ってみた
こんにちは!AWS事業本部コンサルティング部のたかくに(@takakuni_)です。
今回は、Global Acceleratorエンドポイント作成時にAWS側で作成されるGlobal Accelerator用のセキュリティグループをALBのセキュリティグループに紐づけるカスタムリソースを作ってみました。
なぜそんなことをしているのか
Global Acceleratorエンドポイントグループ作成時、Global Acceleratorはエンドポイントが存在するVPCごとに1つずつセキュリティグループを作成します。
つまり、セキュリティグループを作成するのはCloudFormationではなく、Global Accelerator側となります。
そのため、Global AcceleratorエンドポイントとALBを同時に作成するテンプレートの場合、CloudFormation側でGlobal Acceleratorのセキュリティグループを定義していないので、ALBのセキュリティグループでGlobal Acceleratorのセキュリティグループを許可するルールが作成できない事象が発生します。
もちろんですが、Global Acceleratorエンドポイントグループを見ても、返り値としてセキュリティグループを取得することができませんでした。
AWS::GlobalAccelerator::EndpointGroup
今回は、CloudFormationテンプレートを利用して1発で設定する方法が2つあったのでご紹介します。
方法1(非推奨)
方法1は、Global Acceleratorエンドポイントグループ作成前に、セキュリティグループを作ってしまう方法です。
Global Acceleratorで作成されるセキュリティグループの名前は、私が検証した限り「GlobalAccelerator」と名前がついたセキュリティグループが対象VPCに作成されていました。
そのため、次のようにGlobal Acceleratorエンドポイントグループ作成前にセキュリティグループを作成します。
Resources:
AlbSg:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VpcId
GroupName: !Sub "${PrjPrefix}-alb-sg"
GroupDescription: !Sub "${PrjPrefix}-alb-sg"
Tags:
- Key: Name
Value: !Sub "${PrjPrefix}-alb-sg"
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub "${PrjPrefix}-alb"
Type: application
IpAddressType: ipv4
Scheme: internet-facing
SecurityGroups:
- !GetAtt AlbSg.GroupId
Subnets: !Ref SubnetIds
Tags:
- Key: Name
Value: !Sub "${PrjPrefix}-alb"
# Global Accelerator用のSGを先に作る
GlobalAcceleratorSg:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VpcId
GroupName: "GlobalAccelerator" # 名前は固定
GroupDescription: !Sub "${PrjPrefix}-ga-sg"
Tags:
- Key: Name
Value: !Sub "${PrjPrefix}-ga-sg"
AlbFromGaIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !GetAtt AlbSg.GroupId
Description: GlobalAccelerator Communication
FromPort: 80
ToPort: 80
IpProtocol: tcp
SourceSecurityGroupId: !GetAtt GlobalAcceleratorSg.GroupId
AcceleratorListeneralb:
Type: AWS::GlobalAccelerator::EndpointGroup
Properties:
EndpointGroupRegion:
Ref: AWS::Region
ListenerArn:
Fn::GetAtt:
- AcceleratorListener
- ListenerArn
EndpointConfigurations:
- EndpointId:
Ref: Alb
DependsOn:
- GlobalAcceleratorSg # 明示的に依存関係を持たせる
ただし、以下のドキュメントにある通り、Global Acceleratorが作成するセキュリティグループを操作するのは非推奨とされています。
グローバルアクセラレータによって作成されたセキュリティグループ
グローバルアクセラレータとセキュリティグループを使用する場合は、次の情報とベストプラクティスを確認してください。
- Global Accelerator は、Elastic Network インターフェイスに関連付けられているセキュリティグループを作成します。システムによって禁止されるわけではありませんが、これらのグループのセキュリティグループ設定を編集しないでください。
- グローバルアクセラレータは、作成したセキュリティグループを削除しません。ただし、Global Accelerator では、アカウントのアクセラレータのエンドポイントでelastic network interface が使用されていない場合、エラスティックネットワークインターフェイスは削除されます。
- Global Accelerator によって作成されたセキュリティグループは、保守する他のセキュリティグループのソースグループとして使用できますが、Global Accelerator は VPC で指定したターゲットにのみトラフィックを転送します。
- Global Accelerator で作成されたセキュリティグループのルールを変更すると、エンドポイントが異常になることがあります。その場合は、AWS サポート詳細については、
- グローバルアクセラレータは、VPC ごとに特定のセキュリティグループを作成します。特定の VPC 内のエンドポイント用に作成された Elastic ネットワークインターフェイスは、elastic network interface が関連付けられているサブネットに関係なく、すべて同じセキュリティグループを使用します。
クライアント IP アドレスの保存に関するベストプラクティス
方法2(オススメ)
というわけで、Global Acceleratorが作成するセキュリティグループを操作せずに設定する方法が方法2になります。
具体的には、Global Acceleratorが作成したセキュリティグループIDを取得してALBに許可するカスタムリソースになります。
以下のようにカスタムリソースとして設定することで、Global Acceleratorの非推奨項目を回避しつつALBに設定ができました。
AWSTemplateFormatVersion: "2010-09-09"
Description: Terraform pipeline template.
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Project Name Prefix"
Parameters:
- PrjPrefix
- Label:
default: "Network Configration"
Parameters:
- VpcId
- SubnetIds
Parameters:
PrjPrefix:
Type: String
VpcId:
Type: AWS::EC2::VPC::Id
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Resources:
# ALB
AlbSg:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VpcId
GroupName: !Sub "${PrjPrefix}-alb-sg"
GroupDescription: !Sub "${PrjPrefix}-alb-sg"
Tags:
- Key: Name
Value: !Sub "${PrjPrefix}-alb-sg"
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub "${PrjPrefix}-alb"
Type: application
IpAddressType: ipv4
Scheme: internet-facing
SecurityGroups:
- !GetAtt AlbSg.GroupId
Subnets: !Ref SubnetIds
Tags:
- Key: Name
Value: !Sub "${PrjPrefix}-alb"
AlbTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckPath: /healthcheck
HealthCheckPort: 80
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 6
HealthyThresholdCount: 3
Name: !Sub "${PrjPrefix}-alb"
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 3
TargetType: ip
VpcId: !Ref VpcId
AlbListerner:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref AlbTargetGroup
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
# GlobalAccelerator
Accelerator:
Type: AWS::GlobalAccelerator::Accelerator
Properties:
Name: !Sub "${PrjPrefix}-ga"
Enabled: true
AcceleratorListener:
Type: AWS::GlobalAccelerator::Listener
Properties:
AcceleratorArn:
Fn::GetAtt:
- Accelerator
- AcceleratorArn
PortRanges:
- FromPort: 80
ToPort: 80
Protocol: TCP
ClientAffinity: NONE
AcceleratorListenerAlb:
Type: AWS::GlobalAccelerator::EndpointGroup
Properties:
EndpointGroupRegion:
Ref: AWS::Region
ListenerArn:
Fn::GetAtt:
- AcceleratorListener
- ListenerArn
EndpointConfigurations:
- EndpointId:
Ref: Alb
# Security Group
ModifySecrityGroupRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: /
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
- "arn:aws:iam::aws:policy/job-function/NetworkAdministrator"
RoleName: !Sub "${PrjPrefix}-modify-security-group"
ModifySecrityGroupLog:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${PrjPrefix}-modify-security-group"
ModifySecrityGroupFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${PrjPrefix}-modify-security-group"
Code:
ZipFile: |
import json
import boto3
import cfnresponse
def handler(event, context):
vpc_id = event['ResourceProperties']['VpcId']
alb_sg_id = event['ResourceProperties']['AlbSgId']
alb_lintener_port = int(event['ResourceProperties']['AlbListernerPort'])
try:
if event['RequestType'] == 'Create':
ec2 = boto3.client('ec2')
globalaccelerator_sg_id = ec2.describe_security_groups(
Filters=[
{
'Name': 'vpc-id',
'Values': [ vpc_id ]
},
{
'Name': 'group-name',
'Values': [ 'GlobalAccelerator' ]
}
]
)['SecurityGroups'][0]['GroupId']
response = ec2.authorize_security_group_ingress(
GroupId = alb_sg_id,
IpPermissions = [
{
'FromPort': alb_lintener_port,
'ToPort': alb_lintener_port,
'IpProtocol': 'tcp',
'UserIdGroupPairs': [
{
'GroupId': globalaccelerator_sg_id,
'Description': 'GlobalAccelerator Communication'
}
]
}
]
)
cfnresponse.send(event, context, cfnresponse.SUCCESS, response)
if event['RequestType'] == 'Delete':
ec2 = boto3.client('ec2')
globalaccelerator_sg_id = ec2.describe_security_groups(
Filters=[
{
'Name': 'vpc-id',
'Values': [ vpc_id ]
},
{
'Name': 'group-name',
'Values': [ 'GlobalAccelerator' ]
}
]
)['SecurityGroups'][0]['GroupId']
response = ec2.revoke_security_group_ingress(
GroupId = alb_sg_id,
IpPermissions = [
{
'FromPort': alb_lintener_port,
'ToPort': alb_lintener_port,
'IpProtocol': 'tcp',
'UserIdGroupPairs': [
{
'GroupId': globalaccelerator_sg_id,
'Description': 'GlobalAccelerator Communication'
}
]
}
]
)
cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Response': 'Success'})
if event['RequestType'] == 'Update':
cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Response': 'Success'})
except Exception as e:
print(e)
cfnresponse.send(event, context, cfnresponse.FAILED, {})
Handler: index.handler
MemorySize: 128
Role: !GetAtt ModifySecrityGroupRole.Arn
Runtime: "python3.9"
Timeout: 60
Tags:
- Key: "Name"
Value: !Sub "${PrjPrefix}-modify-security-group"
DependsOn: ModifySecrityGroupLog
ModifySecrityGroup:
Type: Custom::ModifySecrityGroup
Properties:
ServiceToken: !GetAtt ModifySecrityGroupFunction.Arn
VpcId: !Ref VpcId
AlbSgId: !GetAtt AlbSg.GroupId
AlbListernerPort: 80
DependsOn:
- AcceleratorListenerAlb
まとめ
以上、「Global AcceleratorのセキュリティグループをALBのセキュリティグループで許可するカスタムリソース作ってみた」でした。
Global Accelerator利用時に参考になるととても嬉しいです。
以上、AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!